#pragma once

#include <atlimage.h>
#include <shlwapi.h>

namespace gdip
{
	inline CLSID FindCodecForExtension(LPCTSTR pszExtension)
	{
		UINT nCodecs;
		UINT nBytes;
		Gdiplus::Status status;

		status = Gdiplus::GetImageEncodersSize(&nCodecs, &nBytes);
		if( status != Gdiplus::Ok )
			return CLSID_NULL;

		USES_CONVERSION_EX;
		Gdiplus::ImageCodecInfo* pCodecs = static_cast<Gdiplus::ImageCodecInfo*>(_ATL_SAFE_ALLOCA(nBytes, _ATL_SAFE_ALLOCA_DEF_THRESHOLD));

		if(pCodecs == NULL)
			return CLSID_NULL;

		status = Gdiplus::GetImageEncoders(nCodecs, nBytes, pCodecs);
		if( status != Gdiplus::Ok )
			return CLSID_NULL;

		CT2CW pszExtensionW(pszExtension);

		for(UINT iCodec = 0; iCodec < nCodecs; iCodec++)
		{
			CStringW strExtensions(pCodecs[iCodec].FilenameExtension);

			int iStart = 0;
			do
			{
				CStringW strExtension = ::PathFindExtensionW(strExtensions.Tokenize(L";", iStart ));
				if( iStart != -1 )
				{
					if( strExtension.CompareNoCase(pszExtensionW) == 0 )
					{
						return pCodecs[iCodec].Clsid;
					}
				}
			}while(iStart != -1);
		}

		return CLSID_NULL;
	}

	inline bool QuickResample(LPCTSTR lpszDstFile, LPCTSTR lpszSrcFile, int nWidth, int nHeight)
	{
		if(lpszDstFile == NULL || lpszSrcFile == NULL || nWidth <= 0 || nHeight <= 0)
			return false;

		Gdiplus::Bitmap bmp(lpszSrcFile);
		if(bmp.GetLastStatus() != Gdiplus::Ok)
			return false;

		Gdiplus::Image* pImg = bmp.GetThumbnailImage(nWidth, nHeight);
		if(pImg == NULL)
			return false;

		CLSID clsidEncoder = FindCodecForExtension(::PathFindExtension(lpszSrcFile));
		pImg->Save(lpszDstFile, &clsidEncoder);

		delete pImg;

		return true;
	}

	inline HBITMAP QuickResample(LPCTSTR lpszSrcFile, int nWidth, int nHeight, bool bKeepRatio = false, COLORREF clrBkgrnd = RGB(0, 0, 0), bool bQuick = true)
	{
		if(lpszSrcFile == NULL || nWidth <= 0 || nHeight <= 0)
			return NULL;

		Gdiplus::Bitmap bmp(lpszSrcFile);
		if(bmp.GetLastStatus() != Gdiplus::Ok)
			return NULL;

		int nFileWidth = bmp.GetWidth();
		int nFileHeight = bmp.GetHeight();

		Gdiplus::Image* pImg = bQuick ? bmp.GetThumbnailImage(nWidth, nHeight) : &bmp;
		if(pImg == NULL)
			return NULL;

		Gdiplus::Bitmap bmpTmp(nWidth, nHeight);
		Gdiplus::Graphics graphics(&bmpTmp);
		if(bKeepRatio)
		{
			CRect rcImg;
			rcImg.right = (float)nHeight * nFileWidth / nFileHeight;
			rcImg.bottom = (float)nWidth * nFileHeight / nFileWidth;

			if(rcImg.right < nWidth)
			{
				rcImg.top = 0;
				rcImg.bottom = nHeight;

				rcImg.left = (nWidth - rcImg.right) / 2;
				rcImg.right += rcImg.left; 
			}
			else
			{
				rcImg.left = 0;
				rcImg.right = nWidth;

				rcImg.top = (nHeight - rcImg.bottom) / 2;
				rcImg.bottom += rcImg.top;
			}

			graphics.DrawImage(&bmp, rcImg.left, rcImg.top, rcImg.Width(), rcImg.Height());
		}
		else
		{
			graphics.DrawImage(&bmp, 0, 0, nWidth, nHeight);
		}

		if(bQuick)
			delete pImg;

		HBITMAP hBitmap = NULL;
		bmpTmp.GetHBITMAP(Gdiplus::Color(GetRValue(clrBkgrnd), GetGValue(clrBkgrnd), GetBValue(clrBkgrnd)), &hBitmap);

		return hBitmap;
	}

	inline bool QuickResample(LPCTSTR lpszDstFile, LPCTSTR lpszSrcFile, int nWidth, int nHeight, bool bKeepRatio, COLORREF clrBkgrnd = RGB(0, 0, 0), bool bQuick = true)
	{
		HBITMAP hBmp = QuickResample(lpszSrcFile, nWidth, nHeight, bKeepRatio, clrBkgrnd, bQuick);
		if(hBmp == NULL)
			return false;

		CImage img;
		img.Attach(hBmp);
		HRESULT hr = img.Save(lpszDstFile);
		
		return SUCCEEDED(hr);
	}
}